<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Ultimate Online Guitar Tuner</title>
<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
  ga('create', 'UA-3425939-7', 'auto');
  ga('send', 'pageview');
</script>
<style type="text/css">
<!--
body { /*background-color:#ededed; */
    background: url("./images/wallpaper.jpg");
    background-attachment:fixed;
    background-repeat:no-repeat; }
h1 { display:block; width:620px; margin:20px auto; paddVing-bottom:20px; font:norm2al 24px/30px Georgia, "Times New Roman", Times, serif; color:#333; text-shadow: 1px 2px 3px #ccc; border-bottom:1px solid #cbcbcb; }
#container { width:600px; margin:0 auto; }
#content{
    background:rgba(255,255,255,0.3);
    width: 80%;
    height: 60%;
    padding: 30px;
    margin:30px 100px;
    border-radius: 5px;
}
#myCanvas { background: rgba(54, 54, 54, 0.69); border:1px solid rgba(176, 176, 176, 0.63);border-radius: 8px ;margin-bottom: 25px}
#nav { display:block; width:100%; text-align:center; }
#nav li { display:block; font-weight:bold; line-height:21px; text-shadow:1px 1px 1px #fff; width:100px; height:21px; paddVing:5px; margin:0 10px; background: #787878; border:1px solid rgba(255, 255, 255, 0.16); -moz-border-radius:4px;-webkit-border-radius:4px; border-radius:4px; float:left; }
#nav li a { color: rgba(232, 232, 232, 0.17); display:block; text-decoration:none; width:100%; height:100%; }
-->
</style>
</head>
<script>

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();

analyser.fftSize = 2048;
var bufferLength = analyser.fftSize;
var dataArray = new Uint8Array(bufferLength);
analyser.getByteTimeDomainData(dataArray);
var myCanvas;

function Goertzel(buffer, sampleRate, freq)
{
    var w = 2 * 3.141592654 * freq / sampleRate;
    var cr = Math.cos(w);
    var ci = Math.sin(w);
    var coeff = 2 * cr;

    var sprev = 0;
    var sprev2 = 0;
    for(var n=0;n<buffer.length;n++)
    {
        var s = buffer[n] + coeff * sprev - sprev2;
        sprev2 = sprev;
        sprev = s;
    }
    var power = sprev2 * sprev2 + sprev * sprev - coeff * sprev * sprev2;
    
    return power;
}


function drawScope(buffer, sampleRate) 
{
    canvasCtx.lineWidth = 2;
    canvasCtx.strokeStyle = 'rgba(255,255,255,0)';

    canvasCtx.beginPath();

    var sliceWidth = myCanvas.width * 1.0 / bufferLength;
    var x = 0;

    for(var i = 0; i < bufferLength; i++) 
    {
        var v = buffer[i];// / 128.0;
        var y = v * myCanvas.height/2;
        y += myCanvas.height/2;
        //y += myCanvas.height/2;

        if(i === 0) {
          canvasCtx.moveTo(x, y);
        } else {
          canvasCtx.lineTo(x, y);
        }

        x += sliceWidth;
    }

    canvasCtx.lineTo(myCanvas.width, myCanvas.height/2);
    canvasCtx.stroke();
}

function drawGuitar(buffer, sampleRate) 
{
    canvasCtx.beginPath();
    for(var s=0;s<6;s++)
    {
        var x = (myCanvas.width/7)*(s+1);
        canvasCtx.moveTo(x, 300);
        canvasCtx.lineTo(x, 0);
    }
    canvasCtx.stroke();

    var freqs = [ 329, 246,196,146,110,82];
    canvasCtx.beginPath();
    
    for(var s=0;s<6;s++)
    {
        var x = (myCanvas.width/7)*(s+1);
        for(var f=-10;f<10;f++)
        {
            var power = Math.sqrt(Goertzel(buffer, sampleRate, freqs[s] + f*2));
            
            canvasCtx.moveTo(x+f*3, 300);
            canvasCtx.lineTo(x+f*3, 300-power);
        }
    }
    canvasCtx.stroke();
};

var noteName = [  'C', 'C#', 'D','D#', 'E', 'F', 'F#', 'G', 'G#',  'A','A#', 'B'];
function NoteNameFromNote(note)
{
    note += 8;
    var octave = Math.floor(note / noteName.length)
    return noteName[note % noteName.length] + octave;
}

function FreqFromNote(note)
{
    return 440.0*Math.pow(2, (note-49)/12);
}

function DrawPiano(buffer, sampleRate) 
{
    var noteFreqs = [ 329, 246,196,146,110,82];
    var noteNumber = [ 20, 25, 30, 35, 39, 44 ];
    
    canvasCtx.font="15px ti92pluspc";
    canvasCtx.textAlign="center"; 
    canvasCtx.beginPath();
    canvasCtx.strokeStyle = 'rgb(255, 0, 0)';
    
    for(var i=0;i<noteNumber.length;i++)
    {
        note = noteNumber[i]
        var x = (myCanvas.width/88)*(note+1);
        canvasCtx.moveTo(x, 270);
        canvasCtx.lineTo(x, 0);
        
        canvasCtx.fillText(NoteNameFromNote(note),x,290);
    }       
    canvasCtx.stroke();    
    
    canvasCtx.beginPath();
    canvasCtx.strokeStyle = 'rgba(231,231,231,1)';
    for(var note=0;note<87;note++)
    {
        var x = (myCanvas.width/88)*(note+1);
        
        var power = Math.sqrt(Goertzel(buffer, sampleRate, FreqFromNote(note)));
        
        canvasCtx.moveTo(x, 270);
        canvasCtx.lineTo(x, 270-power);
    }
    
    canvasCtx.moveTo(0, 271);
    canvasCtx.lineTo(600, 271);
    
    canvasCtx.stroke();
}

function draw(buffer, sampleRate) 
{
    canvasCtx.clearRect(0,0,600,300);
    canvasCtx.fillStyle = 'rgb(4,4,4)';
    canvasCtx.fillRect(0, 0,canvasCtx.width, canvasCtx.height);

    DrawPiano(buffer, sampleRate) ;
};

var audioContext;

function init()
{
    myCanvas = document.getElementById("myCanvas");
    canvasCtx = myCanvas.getContext('2d');    

    var bufferSize = 4096;

    try 
    {
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        audioContext = new AudioContext();
    } 
    catch(e) 
    {
        alert('Web Audio API is not supported in this browser');
    }

    // Check if there is microphone input.
    try 
    {
        navigator.getUserMedia = navigator.getUserMedia ||
                                 navigator.webkitGetUserMedia ||
                                 navigator.mozGetUserMedia ||
                                 navigator.msGetUserMedia;
        var hasMicrophoneInput = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
    } 
    catch(e) 
    {
        alert("getUserMedia() is not supported in your browser");
    }

    // Create a pcm processing "node" for the filter graph.
    var bufferSize = 4096;
    var myPCMProcessingNode = audioContext.createScriptProcessor(bufferSize, 1, 1);
    myPCMProcessingNode.onaudioprocess = function(e) 
    {
        var input = e.inputBuffer.getChannelData(0);
        var output = e.outputBuffer.getChannelData(0);
        draw(input,  e.inputBuffer.sampleRate);
    }

    canvasCtx.fillStyle = "black";
    canvasCtx.textAlign="center"; 
    canvasCtx.textBaseline="middle";
    canvasCtx.font="40px arial";
    canvasCtx.fillText("Click to start",myCanvas.width/2,myCanvas.height/2);


    var errorCallback = function(e) 
    {
        alert("Error in getUserMedia: " + e);
    };  

    // Get access to the microphone and start pumping data through the  graph.
    navigator.getUserMedia({audio: true}, function(stream) 
    {
        var microphone = audioContext.createMediaStreamSource(stream);
        microphone.connect(myPCMProcessingNode);
        myPCMProcessingNode.connect(audioContext.destination);
    }, errorCallback);
}
</script>


<body onload="init()">
<div id="content">
<h1>在线吉他调音器（Online Guitar Tuner）</h1>
<div id="container">

<canvas id="myCanvas" onclick="audioContext.resume()" width="600" height="300"></canvas>

<div id="text"></div>

<h2>Instructions:</h2>
    <ul>
    <li>每个小节对应于钢琴音阶中的每个音符。</li>
    <li>条形的高度决定音符的力量。</li>
    <li>红线显示打开时吉他的6弦音符。</li>
    <li>第六弦的调音总是很棘手，尝试将手指放在第十弦上进行调音，这应该会给您D3。</li>
    </ul>
</br>
</br>
</div>
</div>
</body>
</html>
